package game

import (
	"fmt"
	"math"
	"math/rand"
	"time"

	. "gddgame.cc/galaxy/common/dsl/game"
	. "gddgame.cc/galaxy/common/dsl/game/reward"
	. "gddgame.cc/galaxy/common/dsl/game/task"
	"gddgame.cc/galaxy/common/service/playbook"
	"gddgame.cc/galaxy/common/service/playbook/game_user"
	"gddgame.cc/galaxy/project/star/star/model"
	"gddgame.cc/galaxy/utils"
)

const (
	KillStatKey  string = model.KillStat
	Item1StatKey string = "item1"
	Item2StatKey string = "item2"
	Item3StatKey string = "item3"
	Item4StatKey string = "item4"
	DeadStatKey  string = model.DeadStat
)

var (
	itemStatKey = []string{Item1StatKey, Item2StatKey, Item3StatKey, Item4StatKey}
)

type ActStartMode = byte

const (
	// 一次性生成所有怪物
	OneTimeAct ActStartMode = iota
	// 一个个生成怪物，按间隔时间
	PartTimeAct
	// 随机时间生成怪物
	RandomTimeAct
)

const (
	InfoUiEvent byte = iota
	ProcessUiEvent
)

const (
	ModeUiKey byte = iota
	NumberUiKey
)

type ActEndMode = byte

const (
	// 根据时间完成
	TimeAct ActEndMode = iota
	// 所有怪物死亡
	KillAllAct
	FactorAct
)

type Act struct {
	// 启动模式
	StartMode ActStartMode
	// 出怪间隔时间 - 部分怪物生成模式
	GapTime time.Duration
	// 出怪间隔数量 - 部分怪物生成模式
	GapNumber int32

	// 结束模式
	EndMode ActEndMode
	// 持续时间 - 时间结束模式
	DurationTime time.Duration

	//RL     int32
	//Region []SafeRegion

	// 怪物数量
	AiNumber int32
	// 是否有boss
	HasBoss bool

	// 道具数量
	ObjectNumber int32

	// 运行中信息
	// 启动时间
	StartTime time.Time
	EndFactor []Factor

	// 已创建怪物数
	MakeAiNumber int
	// 最后创建时间
	LastMake time.Time
	// 目标战斗力
	Power int

	// 已创建道具
	MakeObjectNumber int32
}

//type Position struct {
//	X float32
//	Y float32
//	Z float32
//}

//var (
//	playerPositions = map[int][]Position{
//		1: {{0, 0, 0}},
//		2: {{-1, 0, 0}, {1, 0, 0}},
//		3: {{-1, 0, 1}, {1, 0, 1}, {0, 0, -1}},
//		4: {{-1, 0, 1}, {1, 0, 1}, {-1, 0, -1}, {1, 0, -1}},
//		5: {{0, 0, 0}, {-2, 0, 2}, {2, 0, 2}, {-2, 0, -2}, {2, 0, -2}},
//	}
//)

type ZoneScene struct {
	center *Center
	*game_user.PlayerManager

	manager      playbook.SceneManager
	people       int32
	level        int32
	mainTask     *GameTask
	additionTask *GameTask
	taskMap      map[int]*GameTask

	// 失败判断
	failFactor []Factor

	mode model.GameMode

	acts       []*Act
	currentAct *Act
	//playerRegion SafeRegion

	targets      []int
	targetRandom *utils.WeightRandom
	bossRandom   *utils.WeightRandom
	objects      []int
	objectRandom *utils.WeightRandom

	number       int32
	makeAi       []int
	currentIndex int

	// 当前信息
	power   int
	process int
	success bool

	// 能量道具最后make时间
	powerLastMake time.Time
	powerGapTime  time.Duration

	// 歼灭站，记录杀敌数
	total int32

	finish chan<- struct{}
}

func NewZoneScene(center *Center, manager *game_user.PlayerManager, node *model.ZoneNode) *ZoneScene {
	s := &ZoneScene{
		center:        center,
		PlayerManager: manager,
		level:         int32(node.Level),

		mode:         node.Mode,
		mainTask:     node.MainTaskInfo,
		additionTask: node.AdditionTaskInfo,
		// 保存，确保节点更新不影响正在运行中的场景
		taskMap:      map[int]*GameTask{node.AdditionTask: node.AdditionTaskInfo},
		targets:      append(node.Targets, node.Boss...),
		targetRandom: node.TargetRandom,
		bossRandom:   node.BossRandom,
		objects:      node.Objects,
		objectRandom: node.ObjectRandom,

		powerGapTime: time.Second * 10,

		makeAi:  []int{},
		process: 0,
		success: false,
	}
	return s
}

func (scene *ZoneScene) ID() string {
	return "zone"
}

func (scene *ZoneScene) Data() *playbook.Data {
	m := scene.center.GetDataStatMap()
	mm := make(map[int]string, len(m))
	for index, attr := range m {
		mm[index] = attr.Key
	}
	return &playbook.Data{
		StatData:      scene.center.StatData,
		ObjectData:    scene.center.ObjectData,
		TargetData:    scene.center.TargetData,
		RealtimeIndex: []int{scene.center.GetDataStatIndex(KillStatKey), scene.center.GetDataStatIndex(DeadStatKey)},
		DataStatKey:   mm,
	}
}

func (scene *ZoneScene) Bind(manager playbook.SceneManager) {
	scene.manager = manager
}

func (scene *ZoneScene) Ticker() time.Duration {
	return time.Second
}

func (scene *ZoneScene) GenerateTargetList() []int {
	return scene.targets
}

func (scene *ZoneScene) GenerateObjectList() []int {
	return append(scene.objects, scene.center.PowerObjects...)
}

func (scene *ZoneScene) Tasks() map[int]*GameTask {
	return scene.taskMap
}

func (scene *ZoneScene) InitScene(players []*playbook.Player) playbook.InfoArray {
	infos := []playbook.InfoDetail{}
	for index, player := range players {
		v, _ := scene.center.GeneratePlayer(player, scene.UserMap[player.OpenId].(*model.User), index)
		infos = append(infos, playbook.InfoDetail{Key: playbook.TargetKey, Value: v})
		scene.PlayerMap[player.OpenId] = index
	}
	scene.people = int32(len(scene.PlayerMap))
	scene.currentIndex = len(players)
	// 下发地图信息: 可根据zone大小,环境设定
	infos = append(infos, playbook.InfoDetail{Key: playbook.MapKey, Value: &playbook.MapInfo{Number: int(scene.level) * 5, Random: int(time.Now().Unix()), Type: 0}})

	scene.power = model.ZonePowerList[scene.level-1]

	acts := make([]*Act, scene.level*2)
	switch scene.mode {
	case model.AnnihilateTaskMode:
		// 歼灭模式：主任务为战斗结束任务
		total := scene.mainTask.GetGoal()
		objectNumber := int32(0)
		if len(scene.additionTask.Inside.(*model.TaskInside).Objects) > 0 {
			objectNumber = scene.additionTask.GetGoal()
		} else {
			total += scene.additionTask.GetGoal()
		}
		surplus := total
		bossNumber := int32(0)
		if scene.bossRandom.Size() > 0 {
			bossNumber += 1
		}
		surplus += bossNumber
		if surplus < 0 {
			surplus = 20 // int32(len(acts) - 1)
		}
		one := surplus / int32(len(acts)-1)
		if one == 0 {
			one = 1
		}
		objectOne := objectNumber / int32(len(acts)-1)
		for i := 0; i < len(acts)-1; i++ {
			if surplus < one {
				one = surplus
			}
			surplus -= one
			scene.number += one
			acts[i] = &Act{
				StartMode: OneTimeAct,
				GapTime:   2 * time.Second,

				EndMode:      TimeAct,
				DurationTime: 5 * time.Second,

				AiNumber: one,
				HasBoss:  false,

				ObjectNumber: objectOne,
			}
		}
		// 最后出一个boss
		scene.number += bossNumber
		acts[len(acts)-1] = &Act{
			StartMode:    OneTimeAct,
			EndMode:      KillAllAct,
			DurationTime: 5 * time.Second,

			AiNumber: bossNumber,
			HasBoss:  scene.bossRandom.Size() > 0,
		}
		// 下发怪物总数
		infos = append(infos, playbook.InfoDetail{Key: playbook.UiKey, Value: &playbook.UiInfo{Event: InfoUiEvent, Key: ModeUiKey, Value: int(model.AnnihilateTaskMode)}})
		infos = append(infos, playbook.InfoDetail{Key: playbook.UiKey, Value: &playbook.UiInfo{Event: InfoUiEvent, Key: NumberUiKey, Value: int(scene.number)}})

		scene.failFactor = []Factor{{
			Source: PlaybookSource,
			Key:    DeadStatKey,
			Value:  int32(len(players)),
		}}
	case model.DefendTaskMode:
		// 防御模式
		for i := 0; i < len(acts)-1; i++ {
			acts[i] = &Act{
				StartMode:    PartTimeAct,
				GapTime:      5 * time.Second,
				GapNumber:    scene.people * 5,
				EndMode:      TimeAct,
				DurationTime: 3 * time.Second,

				AiNumber: 15,
				HasBoss:  false,
			}
		}
		acts[len(acts)-1] = &Act{
			StartMode:    OneTimeAct,
			EndMode:      KillAllAct,
			DurationTime: 3 * time.Second,

			AiNumber: 2,
			HasBoss:  false,
		}
		scene.failFactor = []Factor{}
	case model.ProtectTaskMode:
		// 护送模式
		for i := 0; i < len(acts)-1; i++ {
			acts[i] = &Act{
				StartMode:    RandomTimeAct,
				GapTime:      2 * time.Second,
				GapNumber:    scene.people,
				EndMode:      TimeAct,
				DurationTime: 3 * time.Second,

				AiNumber: 15,
				HasBoss:  false,
			}
		}
		acts[len(acts)-1] = &Act{
			StartMode:    OneTimeAct,
			EndMode:      FactorAct,
			EndFactor:    scene.mainTask.Factors,
			DurationTime: 3 * time.Second,

			AiNumber: 2,
			HasBoss:  false,
		}
	}
	scene.acts = acts

	return infos
}

func (scene *ZoneScene) nextAct() {
	fmt.Println("act process", scene.process, len(scene.acts)-1)
	if scene.process <= len(scene.acts)-1 {
		scene.currentAct = scene.acts[scene.process]
		scene.currentAct.Power = scene.power
		scene.process += 1
	} else {
		scene.success = true
		scene.finish <- struct{}{}
	}
}

func (scene *ZoneScene) InitPlayer(player *playbook.Player) playbook.InfoArray {
	infos := []playbook.InfoDetail{}
	// 创建用户
	infos = append(infos, playbook.InfoDetail{Key: playbook.IdentityKey, Value: 1})
	infos = append(infos, playbook.InfoDetail{Key: playbook.MakeKey, Value: &playbook.MakeInfo{Id: scene.PlayerMap[player.OpenId], Category: playbook.TargetMake, Relation: playbook.PlayerMake, X: 0, Y: 0, Z: 0}})
	return infos
}

func (scene *ZoneScene) Start(finish chan<- struct{}) {
	scene.finish = finish
	scene.nextAct()
}

func (scene *ZoneScene) UpdatePlayer(player *playbook.Player) playbook.InfoArray {
	infos := []playbook.InfoDetail{}
	switch scene.currentAct.StartMode {
	case OneTimeAct:
		if scene.currentAct.MakeAiNumber < len(scene.makeAi) {
			pre := scene.currentAct.AiNumber / scene.people
			for i := int32(0); i < pre; i++ {
				if scene.currentAct.MakeAiNumber >= len(scene.makeAi) {
					break
				}

				infos = append(infos, playbook.InfoDetail{Key: playbook.MakeKey, Value: scene.makeAiInfo(scene.currentAct)})
			}
		}

	case PartTimeAct:
		if scene.currentAct.MakeAiNumber < len(scene.makeAi) {
			if scene.currentAct.LastMake.IsZero() ||
				time.Now().Sub(scene.currentAct.LastMake) >= scene.currentAct.GapTime {

				pre := scene.currentAct.GapNumber / scene.people
				for i := int32(0); i < pre; i++ {
					if scene.currentAct.MakeAiNumber >= len(scene.makeAi) {
						break
					}

					infos = append(infos, playbook.InfoDetail{Key: playbook.MakeKey, Value: scene.makeAiInfo(scene.currentAct)})
				}
			}
		}

	case RandomTimeAct:
		if scene.currentAct.MakeAiNumber < len(scene.makeAi) {
			// 一半的几率生成一个
			if rand.Int31n(2) > 1 {
				infos = append(infos, playbook.InfoDetail{Key: playbook.MakeKey, Value: scene.makeAiInfo(scene.currentAct)})
			}
		}
	}
	if scene.currentAct.MakeObjectNumber < scene.currentAct.ObjectNumber {
		pre := scene.currentAct.ObjectNumber / scene.people
		for i := int32(0); i < pre; i++ {
			if scene.currentAct.MakeObjectNumber >= scene.currentAct.ObjectNumber {
				break
			}

			infos = append(infos, playbook.InfoDetail{Key: playbook.MakeKey, Value: scene.makeObjectInfo(scene.currentAct)})
		}
	}
	if scene.powerLastMake.IsZero() ||
		time.Now().Sub(scene.powerLastMake) >= scene.powerGapTime {
		scene.powerLastMake = time.Now()
		infos = append(infos, playbook.InfoDetail{Key: playbook.MakeKey, Value: scene.makePowerInfo()})
	}

	return infos
}

func (scene *ZoneScene) makeAiInfo(act *Act) *playbook.MakeInfo {
	x := float32(0)
	y := float32(0)
	z := float32(0)

	// 在出生点中随机一个
	//if act.RL > 0 {
	//	region := act.Region[rand.Int31n(act.RL)]
	//	x = region.X
	//	y = region.Y
	//	z = region.Z
	//}

	act.MakeAiNumber += 1
	act.LastMake = time.Now()
	//fmt.Printf("%d, %d, %#v\n", act.MakeAiNumber, len(scene.makeAi), act)
	return &playbook.MakeInfo{Id: int(scene.makeAi[act.MakeAiNumber-1]), Category: playbook.TargetMake, Relation: playbook.AiMake, X: x, Y: y, Z: z}
}

func (scene *ZoneScene) makeObjectInfo(act *Act) *playbook.MakeInfo {
	act.MakeObjectNumber += 1
	return &playbook.MakeInfo{Id: scene.objectRandom.Random().(int), Category: playbook.ObjectMake, Relation: playbook.BaseMake}
}

func (scene *ZoneScene) makePowerInfo() *playbook.MakeInfo {
	return &playbook.MakeInfo{Id: scene.center.PowerObjectsRandom.Random().(int), Category: playbook.ObjectMake, Relation: playbook.BaseMake}
}

func (scene *ZoneScene) UpdateTicker(languages []string) playbook.InfoArray {
	infos := []playbook.InfoDetail{}
	// 定时更新
	if scene.currentAct.StartTime.IsZero() {
		scene.makeAi = []int{}
		// 启动
		scene.currentAct.StartTime = time.Now()
		// 预先生成所有怪物信息
		gs := scene.center.GenerateGroup(scene.targetRandom, scene.currentAct.AiNumber, scene.currentAct.Power, languages)
		if scene.currentAct.HasBoss {
			// 生成怪物信息
			gs = append(gs, scene.center.GenerateAi(scene.bossRandom.Random().(int), int(float32(scene.currentAct.Power)*1.5), true, languages))
		}
		// 预先生成所有本方Ai
		for _, ai := range gs {
			scene.currentIndex += 1
			scene.makeAi = append(scene.makeAi, scene.currentIndex)
			tmp := make([]interface{}, len(ai))
			for language := range ai {
				ai[language].Id = int(scene.currentIndex)
				tmp[language] = ai[language]
			}
			infos = append(infos, *(&playbook.InfoDetail{Key: playbook.TargetKey}).Language(tmp))
		}

		switch scene.currentAct.EndMode {
		case KillAllAct:
			scene.currentAct.EndFactor = []Factor{{
				Source: PlaybookSource,
				Key:    KillStatKey,
				Value:  scene.number,
			}}
		}
	} else {
		d := scene.UpdateData()
		if len(d) > 0 {
			infos = append(infos, d...)
		}
		switch scene.currentAct.EndMode {
		case TimeAct:
			t := time.Now().Sub(scene.currentAct.StartTime)
			if t >= scene.currentAct.DurationTime {
				// 进入下一幕
				scene.nextAct()
			}
		case KillAllAct:
			fallthrough
		case FactorAct:
			fallthrough
		default:
			if scene.manager.Judge(scene.currentAct.EndFactor) {
				// 进入下一幕
				scene.nextAct()
			}
		}
	}
	return infos
}

func (scene *ZoneScene) UpdateData() playbook.InfoArray {
	infos := []playbook.InfoDetail{}
	switch scene.mode {
	case model.AnnihilateTaskMode:
		total := scene.manager.GetStat(PlaybookSource, KillStatKey)
		if total > scene.total && total <= scene.number {
			scene.total = total
			n := scene.number - total
			if n < 0 {
				n = 0
			}
			infos = append(infos, playbook.InfoDetail{Key: playbook.UiKey, Value: &playbook.UiInfo{Event: ProcessUiEvent, Key: NumberUiKey, Value: int(n)}})
			//fmt.Println("scene", scene.total)
		}
	}
	// 失败判断
	if scene.failFactor != nil && scene.manager.Judge(scene.failFactor) {
		scene.success = false
		scene.finish <- struct{}{}
	}
	return infos
}

func (scene *ZoneScene) Refresh(players []*playbook.Player) {
	switch scene.mode {
	case model.AnnihilateTaskMode:
		l := int32(0)
		for _, player := range players {
			if player.Online {
				l += 1
			}
		}
		scene.failFactor = []Factor{{
			Source: PlaybookSource,
			Key:    DeadStatKey,
			Value:  l,
		}}
	}
}

func (scene *ZoneScene) Finish(pb *playbook.Playbook) {
	// 统计最大战斗记录拥有者
	maxMap := pb.MaxStat()

	// 计算战斗时长
	t := time.Now().Sub(*pb.StartTime()).Seconds()

	prs := make([]*model.PlayerResult, len(pb.Players))
	idMap := make(map[string]int, len(pb.Players))
	index := 0

	for _, player := range pb.Players {
		// 初始化战斗记录
		result := &model.GameResult{}
		result.Time = int(t)
		user := scene.UserMap[player.OpenId].(*model.User)
		pr := &model.PlayerResult{
			Entity: user.Entity,
		}
		// 评分
		scene.Score(pb, player, result)
		// 物品处理:耐久度,消耗
		scene.damageItem(player, user)
		// 合并剧本统计信息到用户
		pr.Stats = scene.mergeStat(pb, player, result)

		prs[index] = pr
		idMap[player.OpenId] = index
		if !player.Exit {
			// 设置获取到的最大统计
			pr.Max = maxMap[player.OpenId]

			// MVP
			for _, key := range pr.Max {
				if key == model.MvpStat {
					pr.MVP = true
				}
			}
		}
		// 合并所有用户信息到Result
		result.Players = prs
		player.Result = result
		index += 1
	}

}

func (scene *ZoneScene) score(player *playbook.Player, source map[string]int32) (int, int, *Reward) {
	reward := &Reward{}
	// 是否完成，影响得分
	if !scene.success {
		return 0, 0, reward
	}

	// 根据评判标准
	level := 0
	score := 0
	if player.Playbook[DeadStatKey] == 0 {
		level += 1
		// 是否完成任务
		if len(player.Tasks) > 0 {
			level += 1
		}
		percent := int32(0)
		if source[KillStatKey] > 0 {
			percent = player.Playbook[KillStatKey] / source[KillStatKey] * 100
		}
		// 超过平均杀伤
		if percent > 100/scene.people {
			level += 1
		}
		// 超过一半杀伤
		if percent > 50 {
			level += 1
		}
	}
	// 获取通关任务奖励
	mainReward := scene.mainTask.Reward
	if mainReward.Gifts != nil {
		for _, value := range mainReward.Gifts {

			gift := GiftType(value.Key)
			logic := model.GiftLogicMap[gift]
			one := &Reward{}
			for i := 0; i < value.Number; i++ {
				child := &Reward{}
				GenerateReward(scene.center.env, logic, child, scene.center.ItemMap)
				one.MergeReward(child)
			}
			reward.MergeReward(one)
		}
		mainReward.Gifts = nil
	}
	reward.MergeReward(&mainReward)
	return level, score, reward
}

func (scene *ZoneScene) IsSuccess() bool {
	return scene.success
}

func (scene *ZoneScene) Score(pb *playbook.Playbook, player *playbook.Player, result *model.GameResult) {
	var reward *Reward
	// 根据统计信息的占比评分
	result.Level, result.Score, reward = scene.score(player, pb.Source)
	if result.Level > model.MaxPlaybookLevel {
		result.Level = model.MaxPlaybookLevel
	}
	result.Reward = reward

	taskReward := &Reward{}
	flag := false
	for _, task := range player.AvailTask {
		if _, ok := player.Tasks[task.Id]; !ok {
			continue
		}
		flag = true
		taskReward.MergeReward(&task.Reward)
	}
	if flag {
		result.TaskReward = taskReward
	}
}

func (scene *ZoneScene) mergeStat(pb *playbook.Playbook, player *playbook.Player, result *model.GameResult) map[string]int {
	// 转换道具统计
	for index, i := range itemStatKey {
		user := scene.UserMap[player.OpenId].(*model.User)
		if index >= len(user.Items) {
			break
		}
		itemId := user.Items[index]
		uitem := user.ItemMap[itemId]
		if uitem == nil {
			continue
		}
		groupList := scene.center.ItemGroupStat[uitem.ItemId]
		for _, group := range groupList {
			if _, ok := player.Playbook[group]; !ok {
				player.Playbook[group] = 0
			}
			player.Playbook[group] += player.Playbook[i]
		}
	}
	// 统计任务数
	player.Playbook[model.TaskStat] = int32(len(player.Tasks))
	// 统计战斗记录，以及场景记录
	player.Playbook[model.PlaybookTimes] = 1
	// 关联战斗时长
	player.Playbook[model.PlaybookTime] = int32(time.Now().Unix()-pb.StartTime().Unix()) / 60
	player.Playbook[scene.ID()] = 1
	if player.TeamNumber > 1 {
		player.Playbook[model.TeamStat] = 1
	}
	if result.Level >= model.MaxPlaybookLevel {
		player.Playbook[model.FullLevelStat] = 1
	}
	// 将用户的playbook数据合并到用户统计数据中去
	stats := make(map[string]int)
	for k, v := range player.Playbook {
		stat, ok := pb.Stats[k]
		if !ok {
			continue
		}

		if stat.IsGameResult {
			// 只保存需要统计的统计项
			stats[stat.Key] = int(v)
		}
	}
	return stats
}
func (scene *ZoneScene) damageItem(player *playbook.Player, user *model.User) {
	if user.Weapon > 0 {
		item := user.ItemMap[user.Weapon]
		item.Durability = int(math.Max(0, float64(item.Durability-1)))
		item.Change = true
	}
	if user.Machine > 0 {
		item := user.ItemMap[user.Machine]
		item.Durability = int(math.Max(0, float64(item.Durability-1)))
		item.Change = true
	}
	for index, key := range user.Items {
		statKey := itemStatKey[index]
		item, ok := user.ItemMap[key]
		if !ok {
			continue
		}
		if scene.center.Items[item.ItemId].IsConsume {
			v := int(player.Playbook[statKey])
			if v <= 0 {
				continue
			}
			item.Number -= v
			if item.Number < 0 {
				item.Number = 0
			}
			item.Change = true
		}
	}
}
